<?php

namespace App\Models;

use App\Admin\Attributes\HasModelUser;
use App\Models\Traits\HasUserAddress;
use App\Models\Traits\HasUserCache;
use App\Models\Traits\HasUserGoldBean;
use App\Models\Traits\HasUserGoldCash;
use App\Models\Traits\HasUserGoldCoin;
use App\Models\Traits\HasUserGoldRecord;
use App\Models\Traits\HasUserGoldCharge;
use App\Models\Traits\HasUserGoldSubsidy;
use App\Models\Traits\HasUserGoldWaste;
use App\Models\Traits\HasUserIdentity;
use App\Models\Traits\HasUserInvite;
use App\Models\Traits\HasUserOrder;
use App\Models\Traits\HasUserStormCache;
use App\Models\Traits\HasUserStormFinishedCache;
use App\Models\Traits\HasUserStormPendingCache;
use App\Models\Traits\HasUserStormRunningCache;
use App\Models\Traits\HasUserStormUnpaidCache;
use App\Models\Traits\HasUserTeam;
use App\Models\Traits\HasUserVipPurchase;
use App\Models\Traits\HasUserWithdraw;
use App\Supports\BankCard\Traits\HasUserBankCard;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Storage;
use Laravel\Sanctum\HasApiTokens;
use Rainsens\Dinglog\Facades\Dinglog;
use Tymon\JWTAuth\Contracts\JWTSubject;
use App\Http\Controllers\Api\V1\Resources\NotificationResource;
use Illuminate\Database\Eloquent\Relations\HasOne;

/**
 * App\Models\User
 *
 * @property int $id
 * @property bool $is_mock
 * @property string|null $mock_name
 * @property int $parent_id
 * @property bool $is_parent
 * @property string $grade
 * @property string $invite_code
 * @property-read int|null $notifications_count
 * @property string|null $name
 * @property string|null $nickname
 * @property string|null $phone
 * @property string|null $signature
 * @property string|null $password
 * @property string|null $pay_code
 * @property \Illuminate\Support\Carbon|null $password_reset_sms_at
 * @property string|null $avatar
 * @property string $gender
 * @property string|null $birthday
 * @property bool $is_gold_intern
 * @property bool $is_gold_master
 * @property string $gold_coins
 * @property string $gold_beans
 * @property string $gold_cashes
 * @property string $gold_subsidies
 * @property int $last_team_prize_user_id
 * @property \Illuminate\Support\Carbon|null $gold_wastes_order_at
 * @property \Illuminate\Support\Carbon|null $gold_wastes_subsidy_at
 * @property string $pre_free_withdraw_quota
 * @property string $dividend_all accumulated total dividend sum
 * @property string $dividend_day accumulated today dividend sum
 * @property string|null $wm_openid
 * @property string|null $wm_session_key
 * @property string|null $wx_unionid
 * @property string|null $wx_openid
 * @property string|null $wx_nickname
 * @property string|null $wx_sex
 * @property string|null $wx_headimgurl
 * @property string|null $wx_country
 * @property string|null $wx_province
 * @property string|null $wx_city
 * @property string|null $wx_qrcode
 * @property string|null $alipay_user_id
 * @property string|null $alipay_user_no
 * @property string|null $alipay_nickname
 * @property string|null $alipay_avatar
 * @property string|null $alipay_province
 * @property string|null $alipay_city
 * @property string|null $real_name
 * @property string|null $cert_number
 * @property string|null $bank_name
 * @property string|null $bank_abbr
 * @property string|null $last_token
 * @property bool $is_locked
 * @property \Illuminate\Support\Carbon|null $created_at
 * @property \Illuminate\Support\Carbon|null $updated_at
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Address[] $addresses
 * @property-read int|null $addresses_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\BankCard[] $bankCards
 * @property-read int|null $bank_cards_count
 * @property-read \Illuminate\Database\Eloquent\Collection|User[] $children
 * @property-read int|null $children_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Comment[] $comments
 * @property-read int|null $comments_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Order[] $consignments
 * @property-read int|null $consignments_count
 * @property-read \App\Models\Freelance|null $freelance
 * @property-read string $parent_avatar
 * @property-read string $parent_name
 * @property-read string $parent_phone
 * @property-read mixed $parent_spend
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\GoldBean[] $goldBeans
 * @property-read int|null $gold_beans_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\GoldCash[] $goldCashes
 * @property-read int|null $gold_cashes_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\GoldCharge[] $goldCharges
 * @property-read int|null $gold_charges_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\GoldChild[] $goldChildren
 * @property-read int|null $gold_children_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\GoldCoin[] $goldCoins
 * @property-read int|null $gold_coins_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\GoldRecord[] $goldRecords
 * @property-read int|null $gold_records_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\GoldSubsidy[] $goldSubsidies
 * @property-read int|null $gold_subsidies_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\GoldWaste[] $goldWastes
 * @property-read int|null $gold_wastes_count
 * @property-read \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\OrderRefund[] $orderRefunds
 * @property-read int|null $order_refunds_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Order[] $orders
 * @property-read int|null $orders_count
 * @property-read User|null $parent
 * @property-read \Illuminate\Database\Eloquent\Collection|\Laravel\Sanctum\PersonalAccessToken[] $tokens
 * @property-read int|null $tokens_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\UpgradeLog[] $upgradeLogs
 * @property-read int|null $upgrade_logs_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Withdraw[] $withdraws
 * @property-read int|null $withdraws_count
 * @method static \Database\Factories\UserFactory factory(...$parameters)
 * @method static Builder|User newModelQuery()
 * @method static Builder|User newQuery()
 * @method static Builder|User query()
 * @method static Builder|User unlocked()
 * @method static Builder|User whereAlipayAvatar($value)
 * @method static Builder|User whereAlipayCity($value)
 * @method static Builder|User whereAlipayNickname($value)
 * @method static Builder|User whereAlipayProvince($value)
 * @method static Builder|User whereAlipayUserId($value)
 * @method static Builder|User whereAlipayUserNo($value)
 * @method static Builder|User whereAvatar($value)
 * @method static Builder|User whereBankAbbr($value)
 * @method static Builder|User whereBankName($value)
 * @method static Builder|User whereBirthday($value)
 * @method static Builder|User whereCertNumber($value)
 * @method static Builder|User whereCreatedAt($value)
 * @method static Builder|User whereDividendAll($value)
 * @method static Builder|User whereDividendDay($value)
 * @method static Builder|User whereGender($value)
 * @method static Builder|User whereGoldBeans($value)
 * @method static Builder|User whereGoldCashes($value)
 * @method static Builder|User whereGoldCoins($value)
 * @method static Builder|User whereGoldSubsidies($value)
 * @method static Builder|User whereGoldWastesOrderAt($value)
 * @method static Builder|User whereGoldWastesSubsidyAt($value)
 * @method static Builder|User whereGrade($value)
 * @method static Builder|User whereId($value)
 * @method static Builder|User whereInviteCode($value)
 * @method static Builder|User whereIsGoldIntern($value)
 * @method static Builder|User whereIsGoldMaster($value)
 * @method static Builder|User whereIsLocked($value)
 * @method static Builder|User whereIsMock($value)
 * @method static Builder|User whereIsParent($value)
 * @method static Builder|User whereLastTeamPrizeUserId($value)
 * @method static Builder|User whereLastToken($value)
 * @method static Builder|User whereMockName($value)
 * @method static Builder|User whereName($value)
 * @method static Builder|User whereNickname($value)
 * @method static Builder|User whereNotificationsCount($value)
 * @method static Builder|User whereParentId($value)
 * @method static Builder|User wherePassword($value)
 * @method static Builder|User wherePasswordResetSmsAt($value)
 * @method static Builder|User wherePayCode($value)
 * @method static Builder|User wherePhone($value)
 * @method static Builder|User wherePreFreeWithdrawQuota($value)
 * @method static Builder|User whereRealName($value)
 * @method static Builder|User whereSignature($value)
 * @method static Builder|User whereUpdatedAt($value)
 * @method static Builder|User whereWmOpenid($value)
 * @method static Builder|User whereWmSessionKey($value)
 * @method static Builder|User whereWxCity($value)
 * @method static Builder|User whereWxCountry($value)
 * @method static Builder|User whereWxHeadimgurl($value)
 * @method static Builder|User whereWxNickname($value)
 * @method static Builder|User whereWxOpenid($value)
 * @method static Builder|User whereWxProvince($value)
 * @method static Builder|User whereWxQrcode($value)
 * @method static Builder|User whereWxSex($value)
 * @method static Builder|User whereWxUnionid($value)
 * @mixin \Eloquent
 * @property bool $has_given_parent_cash
 * @method static Builder|User whereHasGivenParentCash($value)
 */
class User extends Authenticatable implements JWTSubject
{
    use HasApiTokens,
	    HasFactory,
	    Notifiable,
	    HasModelUser,
	    HasUserInvite,
	    HasUserTeam,
	    HasUserOrder,
	    HasUserIdentity,
	    HasUserAddress,
	    HasUserGoldBean,
	    HasUserGoldCoin,
        HasUserGoldCash,
        HasUserGoldSubsidy,
        HasUserGoldWaste,
	    HasUserGoldRecord,
	    HasUserGoldCharge,
	    HasUserWithdraw,
	    HasUserBankCard,
	    HasUserCache,
	    HasUserStormCache,
	    HasUserStormUnpaidCache,
	    HasUserStormPendingCache,
	    HasUserStormRunningCache,
	    HasUserVipPurchase,
	    HasUserStormFinishedCache;

    use Notifiable {notify as laravelNotify;}

    protected $fillable = [
    	'parent_id', 'is_mock', 'mock_name', 'is_parent', 'grade', 'invite_code',

	    'name', 'nickname', 'phone', 'signature', 'password', 'password_reset_sms_at', 'pay_code', 'avatar', 'gender', 'birthday',

	    'is_gold_intern', 'is_gold_master', 'gold_coins', 'gold_beans', 'gold_cashes', 'gold_subsidies',

        'has_given_parent_cash', 'last_team_prize_user_id',

	    'gold_wastes_order_at', 'gold_wastes_subsidy_at', 'dividend_all', 'dividend_day',

	    'wm_openid', 'wm_session_key', 'wx_unionid', 'wx_openid',
	    'wx_nickname', 'wx_sex', 'wx_headimgurl', 'wx_country', 'wx_province', 'wx_city', 'wx_qrcode',
	    'alipay_user_id', 'alipay_nickname', 'alipay_avatar', 'alipay_province', 'alipay_city', 'alipay_user_no',
		'real_name', 'cert_number', 'bank_name' , 'bank_abbr', 'pre_free_withdraw_quota',
	    'last_token', 'is_locked','support_bean','wx_image','alipay_real_name','alipay_number','resold_profit','dividend_parent_reward','dividend_team_reward','team_number','auth_real_name','identify_num','contact_phone','is_show_support','parent_name','admin_user_id','vip_cards','balances','apples','user_level','team_level','vip_expired_at','wechat_account','qq_account','is_agent','rewarded_at','rewards','is_test','upgrade_at','is_stop' 
    ];

    protected $hidden = [
	    'password',
	    'pay_code',
    ];

	protected $casts = [
		'is_mock' => 'boolean',
		'is_parent' => 'boolean',
        'is_gold_intern' => 'boolean',
        'is_gold_master' => 'boolean',
        'has_given_parent_cash' => 'boolean',
		'is_locked' => 'boolean',
	];

    protected $dates = [
        'password_reset_sms_at',
        'gold_wastes_order_at',
        'gold_wastes_subsidy_at',
    ];

	public function getJWTIdentifier()
	{
		return $this->getKey();
	}

	public function getJWTCustomClaims()
	{
		return [];
	}

	public function jsonNotificationTypes(): array
	{
		$types = [];
		foreach (config('site.notifications.types') as $type => $content) {
			$filter = config("site.notifications.types.{$type}.notification");
			$latest = $this->notifications()->where('type', $filter)->latest()->first();
			$unreadCount = $this->notifications()->where('type', $filter)->whereNull('read_at')->count();
			$types[] = [
				'type' => $type,
				'name' => $content['name'],
				'icon' => Storage::url($content['icon']),
				'latest' => $latest ? new NotificationResource($latest) : '',
				'unread_count' => $unreadCount,
			];
		}
		return ['data' => $types];
	}

	public function notify($instance)
	{
		if (method_exists($instance, 'toDatabase')) {
			$this->increment('notifications_count');
		}
		$this->laravelNotify($instance);
	}

	public function markOneAsRead($notification)
	{
		$notification->markAsRead();
		$this->decrement('notifications_count');
	}

	public function markTypeAsRead(string $type)
	{
		$unread = $this->unreadNotifications()->where('type', config("site.notifications.types.{$type}.notification"));
		$unreadCount = $unread->count();
		$unread->update(['read_at' => now()]);
		if ($this->notifications_count >= $unreadCount) {
			$this->notifications_count -= $unreadCount;
		} else {
			$this->notifications_count = 0;
		}
		$this->save();
	}

	public function markAllAsRead()
	{
		$this->notifications_count = 0;
		$this->save();
		$this->unreadNotifications->markAsRead();
	}

	public function searchProductCacheKey(): string
	{
		return "user_{$this->id}_product_search";
	}

	public function getParentPhoneAttribute(): string
	{
		$parent = $this->parent;
		return $parent ? $parent->phone : '';
	}

	/*public function getParentNameAttribute(): string
	{
		$parent = $this->parent;
		return $parent ? $parent->name : '';
	}*/

	public function getParentAvatarAttribute(): string
	{
		$parent = $this->parent;
		return $parent ? (string)$parent->avatar : '';
	}

	public function getParentSpendAttribute()
	{
		$parent = $this->parent;
		return $parent ? $parent->spend : 0;
	}

	public function scopeUnlocked(Builder $builder): Builder
	{
		return $builder->where('is_locked', false);
	}

	public function hasMultiAliAccounts(): bool
	{
		$accounts = $this->withdraws()
			->whereNotNull('alipay_user_id')
			->groupBy('alipay_user_id')
			->get();

		return $accounts->count() > 1;
	}

	public function hasCertNumber()
	{
		return $this->cert_number;
	}

	public function isWithdrawSafe($money = null): bool
	{
		$ip = request()->ip();

		if ($this->hasNoValidGoldCharge()) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 1 (IP: {$ip})");
			return false;
		} elseif ($this->hasNoValidGoldRecord()) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 2 (IP: {$ip})");
			return false;
		} elseif ($this->hasNoGoldBeansRecords()) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 3 (IP: {$ip})");
			return false;
		} elseif ($this->hasMultiAliAccounts()) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 4 (IP: {$ip})");
			return false;
		} elseif ($this->goldCoins()->sum('money') < -0.01) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 5 (IP: {$ip})");
			return false;
		} elseif ($this->goldBeans()->sum('beans') < -0.01) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 6 (IP: {$ip})");
		} elseif ($money and $money > $this->goldRecords()->whereNotNull('paid_at')->sum('resell_price')) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 7 (IP: {$ip})");
			return false;
		}

		return true;
	}

	public function isWithdrawDanger($money = null): bool
	{
		return !$this->isWithdrawSafe();
	}

	public function isWithdrawSafeBySandpay(): bool
	{
		$ip = request()->ip();

		if ($this->hasNoValidGoldCharge()) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 1 (IP: {$ip})");
			return false;
		} elseif ($this->hasNoValidGoldRecord()) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 2 (IP: {$ip})");
			return false;
		} elseif ($this->hasNoGoldBeansRecords()) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 3 (IP: {$ip})");
			return false;
		} elseif (!$this->hasCertNumber()) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 4 (IP: {$ip})");
			return false;
		} elseif ($this->goldCoins()->sum('money') < -0.01) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 5 (IP: {$ip})");
			return false;
		} elseif ($this->goldBeans()->sum('beans') < -0.01) {
			Dinglog::text("User: {$this->id} Risk Withdraw: 6 (IP: {$ip})");
		}

		return true;
	}

	public function isWithdrawDangerBySandpay(): bool
	{
		return !$this->isWithdrawSafeBySandpay();
	}

	public function parent(): BelongsTo
	{
		return $this->belongsTo(User::class, 'parent_id', 'id');
	}

	public function white()
	{
		return $this->hasMany(WhiteList::class);
	}

	public function detect()
	{
		return $this->hasMany(DetectList::class);
	}

	public function detectTest()
	{
		return $this->hasMany(DetectTest::class);
	}

	public function children(): HasMany
	{
		return $this->hasMany(User::class, 'parent_id', 'id');
	}

	public function addresses(): HasMany
	{
		return $this->hasMany(Address::class);
	}

	public function goldCharges(): HasMany
	{
		return $this->hasMany(GoldCharge::class);
	}

	public function goldBeans(): HasMany
	{
		return $this->hasMany(GoldBean::class);
	}

	public function userApples(): HasMany
	{
		return $this->hasMany(Apple::class);
	}

	public function childrenApples(): HasMany
	{
		return $this->hasMany(Apple::class,'children_id','id');
	}

	public function balances(): HasMany
	{
		return $this->hasMany(Balance::class);
	}

	public function rewards(): HasMany
	{
		return $this->hasMany(Reward::class);
	}

	public function childrenRewards(): HasMany
	{
		return $this->hasMany(Reward::class,'children_id','id');
	}

	public function vipCards(): HasMany
	{
		return $this->hasMany(VipCard::class);
	}


	public function goldCoins(): HasMany
	{
		return $this->hasMany(GoldCoin::class);
	}

	public function fromGoldCoins(): HasMany
	{
		return $this->hasMany(GoldCoin::class,'from_user_id','id');
	}

	public function goldCashes(): HasMany
	{
		return $this->hasMany(GoldCash::class);
	}

	public function goldSubsidies(): HasMany
	{
		return $this->hasMany(GoldSubsidy::class);
	}

	public function goldWastes(): HasMany
	{
		return $this->hasMany(GoldWaste::class);
	}

	public function goldRecords(): HasMany
	{
		return $this->hasMany(GoldRecord::class);
	}

	public function goldChildren(): HasMany
	{
		return $this->hasMany(GoldChild::class, 'user_id', 'id');
	}

	public function upgradeLogs(): HasMany
	{
		return $this->hasMany(UpgradeLog::class);
	}

	public function orders(): HasMany
	{
		return $this->hasMany(Order::class);
	}

	public function relationUsers(): HasMany
	{
		return $this->hasMany(RelationUser::class,'parent_id','id');
	}

	public function orderRefunds(): HasMany
	{
		return $this->hasMany(OrderRefund::class);
	}

	public function comments(): HasMany
	{
		return $this->hasMany(Comment::class);
	}

	public function withdraws(): HasMany
	{
		return $this->hasMany(Withdraw::class);
	}

	public function bankCards(): HasMany
	{
		return $this->hasMany(BankCard::class);
	}

	public function consignments(): HasMany
	{
		return $this->hasMany(Order::class, 'consignment_user_id', 'id');
	}

	public function freelance(): HasOne
	{
		return $this->hasOne(Freelance::class);
	}

	public function vipPurchases()
	{
		return $this->hasMany(VipPurchase::class, 'user_id', 'id');
	}

	public function appleTreeRecords()
	{
		return $this->hasMany(AppleTreesRecord::class, 'user_id', 'id');
	}

	public function productLinks()
	{
		return $this->hasMany(ProductLink::class, 'user_id', 'id');
	}

	
}
